
import { IPiece, Result } from "@/third/chinese-chess/src";
import { ChessBoard, ChessPiece, ChessPieceType, ChessTeam } from "@/third/chinese-chess/src/demo/chess";
import { PieceNode } from "./PieceNode";

export class ChessNode {
  team = ChessTeam.Wht;
  x = 1;
  y = 1;
  board = new ChessBoard();
  bg_canvas?: HTMLCanvasElement | null;
  bg_ctx?: CanvasRenderingContext2D | null;
  fg_canvas?: HTMLCanvasElement | null;
  fg_ctx?: CanvasRenderingContext2D | null;
  cell_size = 50;
  cavans_scale: number = 1;

  get bw() { return this.board?.width || 0; }
  get bh() { return this.board?.height || 0; }
  get width(): number {
    return (0.2 + this.bw) * this.cell_size;
  }
  get height(): number {
    return (0.2 + this.bh) * this.cell_size;
  }
  init(
    bg_canvas: HTMLCanvasElement | null,
    fg_canvas: HTMLCanvasElement | null
  ) {
    window.removeEventListener('mousemove', this.on_mousemove);
    this.fg_canvas?.removeEventListener('pointermove', this.on_pointermove);
    this.fg_canvas?.removeEventListener('pointerdown', this.on_pointerdown);
    this.bg_canvas = bg_canvas;
    this.fg_canvas = fg_canvas;
    this.bg_ctx = bg_canvas?.getContext('2d');
    this.fg_ctx = fg_canvas?.getContext('2d');
    window.addEventListener('mousemove', this.on_mousemove);
    this.fg_canvas?.addEventListener('pointermove', this.on_pointermove);
    this.fg_canvas?.addEventListener('pointerdown', this.on_pointerdown);
  }
  hover?: IPiece<ChessTeam, ChessPieceType> | null = null;
  picked?: IPiece<ChessTeam, ChessPieceType> | null = null;
  protected _mouse_x = Number.MAX_SAFE_INTEGER
  protected _mouse_y = Number.MAX_SAFE_INTEGER
  on_mousemove = (e: MouseEvent) => {
    if (e.target === this.fg_canvas) {
      this._mouse_x = e.offsetX;
      this._mouse_y = e.offsetY;
    } else {
      this._mouse_x = Number.MAX_SAFE_INTEGER;
      this._mouse_y = Number.MAX_SAFE_INTEGER;
    }
  }
  is_out_of_board(cx: number, cy: number) {
    const cbw = this.bw * this.cell_size;
    const cbh = this.bh * this.cell_size;
    return (
      cx < 4 + this.x ||
      cy < 4 + this.y ||
      cx > 8 + this.x + cbw ||
      cy > 8 + this.y + cbh
    )
  }
  get_piece_by_cx_cy(cx: number, cy: number) {
    const [a, b] = this.get_b_xy(cx, cy);
    const bx = Math.floor(a);
    const by = Math.floor(b);
    return this.board.get_piece_by_xy(bx, by);
  }
  on_pointermove = (e: PointerEvent) => {
    let hover: typeof this.hover;
    const { offsetX: cx, offsetY: cy } = e;
    if (this.is_out_of_board(cx, cy)) {
      hover = null;
    } else {
      const piece = this.get_piece_by_cx_cy(cx, cy);
      hover = (this.board.acting_player?.team === piece?.team) ? piece : void 0;
    }
    if (this.hover === hover) return;
    this.hover = hover;
  };
  on_pointerdown = (e: PointerEvent) => {
    let point_at: typeof this.picked;
    const { offsetX: cx, offsetY: cy } = e;
    if (this.is_out_of_board(cx, cy)) {
      point_at = null;
    } else {
      const piece = this.get_piece_by_cx_cy(cx, cy);
      point_at = (this.board.acting_player?.team === piece?.team) ? piece : void 0;
      point_at = this.picked === point_at ? null : point_at;
    }
    if (this.picked === point_at) return;
    if (!this.picked) {
      this.picked = point_at;
    } else {
      const [bx, by] = this.get_b_xy(cx, cy).map(Math.floor);
      const result = this.board.move_piece(this.picked, bx, by);
      if (result === Result.Resolve) {
        this.board.acting_player = this.board.next_acting_player;
        this.board_dirty = true
        this.picked = null;
      } else {
        this.picked = point_at;
      }
    }
  };
  draw(delta: number, time: number) {
    if (this.board_dirty) {
      this.draw_board(delta, time);
      this.board_dirty = false;
    }
    this.draw_pieces(delta, time);

    if (
      this._mouse_x < Number.MAX_SAFE_INTEGER && this._mouse_y < Number.MAX_SAFE_INTEGER && this.fg_ctx
    ) {
      this.fg_ctx.textAlign = 'left'
      this.fg_ctx.textBaseline = 'top';
      const ox = (window.devicePixelRatio || 1) * 15
      const oy = (window.devicePixelRatio || 1) * 15
      switch (this.board.acting_player?.team) {
        case ChessTeam.Blk: {
          this.fg_ctx.fillStyle = 'black'
          this.fg_ctx.fillText("Black", this._mouse_x + ox, this._mouse_y + oy)
          break;
        }
        case ChessTeam.Wht: {
          this.fg_ctx.fillStyle = 'white'
          this.fg_ctx.fillText("White", this._mouse_x + ox, this._mouse_y + oy)
          break;
        }
      }
    }
  }
  release() {
    this.fg_canvas?.removeEventListener('pointermove', this.on_pointerdown);
    this.fg_canvas?.removeEventListener('pointerdown', this.on_pointermove);
  }
  get_c_xy(b_x: number, b_y: number) {
    if (this.team === ChessTeam.Wht)
      return [
        4 + this.x + (b_x + 0.5) * this.cell_size,
        4 + this.y + (b_y + 0.5) * this.cell_size
      ] as const;
    else
      return [
        4 + this.x + (7.5 - b_x) * this.cell_size,
        4 + this.y + (7.5 - b_y) * this.cell_size
      ] as const;
  }
  get_b_xy(c_x: number, c_y: number) {
    if (this.team === ChessTeam.Wht)
      return [
        (c_x - 4 - this.x) / this.cell_size,
        (c_y - 4 - this.y) / this.cell_size
      ] as const;
    else
      return [
        7.5 - (c_x - 4 - this.x) / this.cell_size,
        7.5 - (c_x - 4 - this.y) / this.cell_size
      ] as const;
  }
  private _piece_node_map = new Map<ChessPiece, PieceNode>();
  private board_dirty = true;

  draw_board(delta: number, time: number) {
    const { bg_canvas, bg_ctx, cell_size, x, y } = this;
    if (!bg_canvas || !bg_ctx) return;

    const { width: cw, height: ch } = bg_canvas
    bg_canvas.width = cw;
    bg_canvas.height = ch;

    bg_ctx.scale(this.cavans_scale, this.cavans_scale);
    bg_ctx.imageSmoothingEnabled = true;
    bg_ctx.imageSmoothingQuality = 'high';
    bg_ctx.fillStyle = 'white';
    bg_ctx.strokeStyle = 'black';
    bg_ctx.lineWidth = 4;

    for (let i = 0; i < 64; ++i) {
      const bx = Math.floor(i % 8);
      const by = Math.floor(i / 8);
      bg_ctx.fillStyle = ((bx + by) % 2) ? 'rgb(128,128,128)' : 'rgb(180,180,180)';
      bg_ctx.fillRect(
        x + 4 + bx * cell_size,
        y + 4 + by * cell_size,
        cell_size,
        cell_size
      );
    }

    bg_ctx.strokeRect(
      x + 2,
      y + 2,
      cell_size * 8 + 4,
      cell_size * 8 + 4
    );
  }

  draw_pieces(delta: number, time: number) {
    const { fg_canvas, fg_ctx, board } = this;
    if (!fg_canvas || !fg_ctx || !board) return;
    const { width: cw, height: ch } = fg_canvas

    fg_canvas.width = cw;
    fg_canvas.height = ch;

    fg_ctx.scale(this.cavans_scale, this.cavans_scale);
    fg_ctx.imageSmoothingEnabled = true;
    fg_ctx.imageSmoothingQuality = 'high';

    for (const p of board.pieces) {
      if (!p) continue;
      let n = this._piece_node_map.get(p);
      if (!n) this._piece_node_map.set(p, n = new PieceNode(this, p));
    }
    const piece_node_arr = Array.from(this._piece_node_map.values()).sort((a, b) => a.z - b.z);
    for (const n of piece_node_arr) {
      if (n.dead) this._piece_node_map.delete(n.piece);
      n.render(delta, time);
    }
  }
}
